package com.symaster.common.event;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @author yinmiao
 * @version 2022-10-28 14:26
 */
public class EventManger {

    private static final Map<String, Set<EventExecutor>> executorMap = new HashMap<>();
    private static final Map<String, ReentrantLock> lockMap = new Hashtable<>();

    public static EventResult eventOccurs(String key, Object... param) {
        return eventOccurs(key, true, param);
    }

    public static EventResult eventOccurs(String key, boolean isWait, Object... param) {
        Event event = new Event();
        event.setData(param);
        return eventOccurs(key, isWait, event);
    }

    private static EventResult eventOccurs(String key, boolean isWait, Event event) {
        Set<EventExecutor> eventExecutors = executorMap.get(key);
        if (eventExecutors == null || eventExecutors.isEmpty()) {
            return null;
        }

        ReentrantLock reentrantLock;
        synchronized (lockMap) {
            reentrantLock = lockMap.computeIfAbsent(key, k -> new ReentrantLock());
        }

        EventExecutor eventExecutor;
        try {
            reentrantLock.lock();
            while (true) {
                List<EventExecutor> collect = eventExecutors.stream().filter(e -> !e.run).collect(Collectors.toList());
                if (!collect.isEmpty()) {
                    eventExecutor = collect.get(0);
                    break;
                }
                if (!isWait) {
                    return null;
                }
                TimeUnit.MILLISECONDS.sleep(1);
            }
            eventExecutor.run = true;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            reentrantLock.unlock();
        }

        try {
            return eventExecutor.handler(event);
        } finally {
            eventExecutor.run = false;
        }
    }

    public static void addListener(String key, EventExecutor executor) {
        Set<EventExecutor> eventExecutors = executorMap.computeIfAbsent(key, k -> new HashSet<>());
        eventExecutors.add(executor);
    }
}
